package com.asgard.cert;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.util.encoders.Base64;

import com.asgard.utils.SerializeUtils;

/**
 * 该类用于描述CSR，即证书签发请求。
 */
public class CertificationRequest {

    static final String PREFIX = "-----BEGIN CERTIFICATE REQUEST-----\n";
    static final String SUFFIX = "\n-----END CERTIFICATE REQUEST-----";

    /**
     * PKCS10格式的CSR
     */
    private PKCS10CertificationRequest csr;

    /**
     * 使用BASE64格式的字符串初始化。
     *
     * @param encoded 字符串
     * @throws IOException 输入输出异常
     */
    public CertificationRequest(String encoded) throws IOException {
        String r = encoded.replaceAll(PREFIX, "");
        r = r.replaceAll(SUFFIX, "");
        r.replaceAll("\r", "").replaceAll("\n", "").trim();
        this.csr = new PKCS10CertificationRequest(Base64.decode(r));
    }

    /**
     * 使用PKCS10格式的字节数组初始化。
     *
     * @param encoded 字节数组
     * @throws IOException 输入输出异常
     */
    public CertificationRequest(byte[] encoded) throws IOException {
        this.csr = new PKCS10CertificationRequest(encoded);
    }

    /**
     * 使用PKCS10CertificationRequest实例初始化。
     *
     * @param csr PKCS10CertificationRequest实例
     */
    public CertificationRequest(PKCS10CertificationRequest csr) {
        this.csr = csr;
    }

    /**
     * 获取PKCS10格式的字节数组。
     *
     * @return PKCS10格式的字节数组
     * @throws IOException 输入输出异常
     */
    public byte[] getEncoded() throws IOException {
        return csr.getEncoded();
    }

    /**
     * 获取PKCS10CertificationRequest实例。
     *
     * @return PKCS10CertificationRequest实例
     */
    public PKCS10CertificationRequest getCsr() {
        return csr;
    }

    /**
     * 设置PKCS10CertificationRequest实例。
     *
     * @param csr PKCS10CertificationRequest实例
     */
    public void setCsr(PKCS10CertificationRequest csr) {
        this.csr = csr;
    }

    /**
     * 反序列化证书签发请求。
     *
     * @param sin 输入流
     * @return 证书签发请求实例
     * @throws IOException 输入输出异常
     */
    public static CertificationRequest readFromStream(InputStream sin) throws IOException {
        byte[] bytes = SerializeUtils.readByteArray(sin);
        return new CertificationRequest(bytes);
    }

    /**
     * 反序列化证书签发请求。
     *
     * @param bytes 字节数组
     * @return 证书签发请求实例
     */
    public static CertificationRequest readFromBytes(byte[] bytes) {
        try (ByteArrayInputStream bin = new ByteArrayInputStream(bytes)) {
            return readFromStream(bin);
        } catch (IOException e) {
            // cannot happen
            return null;
        }
    }

    /**
     * 序列化证书签发请求。
     *
     * @return 序列化后的字节数组
     */
    public byte[] writeToBytes() {
        try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
            writeToStream(bout);
            return bout.toByteArray();
        } catch (IOException e) {
            // cannot happen
            return null;
        }
    }

    /**
     * 序列化证书签发请求。
     *
     * @param sout 输出流
     * @throws IOException 输入输出异常
     */
    public void writeToStream(OutputStream sout) throws IOException {
        SerializeUtils.writeByteArray(sout, getEncoded());
    }

    /**
     * 获取证书签发请求的名称。
     *
     * @return 证书签发请求的名称
     */
    public X500Name getSubject() {
        return csr.getSubject();
    }

    /**
     * 获取证书签发请求中的公钥信息。
     *
     * @return 证书签发请求中的公钥信息
     */
    public SubjectPublicKeyInfo getSubjectPublicKeyInfo() {
        return csr.getSubjectPublicKeyInfo();
    }

    /**
     * 返回Base64格式的字符串。
     *
     * @return Base64格式的字符串
     * @throws IOException 输入输出异常
     */
    public String getBase64String() throws IOException {
        return PREFIX + Base64.toBase64String(this.getEncoded()) + SUFFIX;
    }


}
